( function () {
function LWO2Parser(IFFParser) {
  this.IFF = IFFParser;
}

LWO2Parser.prototype = {
  constructor: LWO2Parser,
  parseBlock: function () {
    this.IFF.debugger.offset = this.IFF.reader.offset;
    this.IFF.debugger.closeForms();
    var blockID = this.IFF.reader.getIDTag();
    var length = this.IFF.reader.getUint32(); // size of data in bytes

    if (length > this.IFF.reader.dv.byteLength - this.IFF.reader.offset) {
      this.IFF.reader.offset -= 4;
      length = this.IFF.reader.getUint16();
    }

    this.IFF.debugger.dataOffset = this.IFF.reader.offset;
    this.IFF.debugger.length = length; // Data types may be found in either LWO2 OR LWO3 spec

    switch (blockID) {
      case 'FORM':
        // form blocks may consist of sub -chunks or sub-forms
        this.IFF.parseForm(length);
        break;
      // SKIPPED CHUNKS
      // if break; is called directly, the position in the lwoTree is not created
      // any sub chunks and forms are added to the parent form instead
      // MISC skipped

      case 'ICON': // Thumbnail Icon Image

      case 'VMPA': // Vertex Map Parameter

      case 'BBOX': // bounding box
      // case 'VMMD':
      // case 'VTYP':
      // normal maps can be specified, normally on models imported from other applications. Currently ignored

      case 'NORM': // ENVL FORM skipped

      case 'PRE ':
      case 'POST':
      case 'KEY ':
      case 'SPAN': // CLIP FORM skipped

      case 'TIME':
      case 'CLRS':
      case 'CLRA':
      case 'FILT':
      case 'DITH':
      case 'CONT':
      case 'BRIT':
      case 'SATR':
      case 'HUE ':
      case 'GAMM':
      case 'NEGA':
      case 'IFLT':
      case 'PFLT': // Image Map Layer skipped

      case 'PROJ':
      case 'AXIS':
      case 'AAST':
      case 'PIXB':
      case 'AUVO':
      case 'STCK': // Procedural Textures skipped

      case 'PROC':
      case 'VALU':
      case 'FUNC': // Gradient Textures skipped

      case 'PNAM':
      case 'INAM':
      case 'GRST':
      case 'GREN':
      case 'GRPT':
      case 'FKEY':
      case 'IKEY': // Texture Mapping Form skipped

      case 'CSYS': // Surface CHUNKs skipped

      case 'OPAQ': // top level 'opacity' checkbox

      case 'CMAP': // clip map
      // Surface node CHUNKS skipped
      // These mainly specify the node editor setup in LW

      case 'NLOC':
      case 'NZOM':
      case 'NVER':
      case 'NSRV':
      case 'NVSK': // unknown

      case 'NCRD':
      case 'WRPW': // image wrap w ( for cylindrical and spherical projections)

      case 'WRPH': // image wrap h

      case 'NMOD':
      case 'NPRW':
      case 'NPLA':
      case 'NODS':
      case 'VERS':
      case 'ENUM':
      case 'TAG ':
      case 'OPAC': // Car Material CHUNKS

      case 'CGMD':
      case 'CGTY':
      case 'CGST':
      case 'CGEN':
      case 'CGTS':
      case 'CGTE':
      case 'OSMP':
      case 'OMDE':
      case 'OUTR':
      case 'FLAG':
      case 'TRNL':
      case 'GLOW':
      case 'GVAL': // glow intensity

      case 'SHRP':
      case 'RFOP':
      case 'RSAN':
      case 'TROP':
      case 'RBLR':
      case 'TBLR':
      case 'CLRH':
      case 'CLRF':
      case 'ADTR':
      case 'LINE':
      case 'ALPH':
      case 'VCOL':
      case 'ENAB':
        this.IFF.debugger.skipped = true;
        this.IFF.reader.skip(length);
        break;

      case 'SURF':
        this.IFF.parseSurfaceLwo2(length);
        break;

      case 'CLIP':
        this.IFF.parseClipLwo2(length);
        break;
      // Texture node chunks (not in spec)

      case 'IPIX': // usePixelBlending

      case 'IMIP': // useMipMaps

      case 'IMOD': // imageBlendingMode

      case 'AMOD': // unknown

      case 'IINV': // imageInvertAlpha

      case 'INCR': // imageInvertColor

      case 'IAXS': // imageAxis ( for non-UV maps)

      case 'IFOT': // imageFallofType

      case 'ITIM': // timing for animated textures

      case 'IWRL':
      case 'IUTI':
      case 'IINX':
      case 'IINY':
      case 'IINZ':
      case 'IREF':
        // possibly a VX for reused texture nodes
        if (length === 4) this.IFF.currentNode[blockID] = this.IFF.reader.getInt32();else this.IFF.reader.skip(length);
        break;

      case 'OTAG':
        this.IFF.parseObjectTag();
        break;

      case 'LAYR':
        this.IFF.parseLayer(length);
        break;

      case 'PNTS':
        this.IFF.parsePoints(length);
        break;

      case 'VMAP':
        this.IFF.parseVertexMapping(length);
        break;

      case 'AUVU':
      case 'AUVN':
        this.IFF.reader.skip(length - 1);
        this.IFF.reader.getVariableLengthIndex(); // VX

        break;

      case 'POLS':
        this.IFF.parsePolygonList(length);
        break;

      case 'TAGS':
        this.IFF.parseTagStrings(length);
        break;

      case 'PTAG':
        this.IFF.parsePolygonTagMapping(length);
        break;

      case 'VMAD':
        this.IFF.parseVertexMapping(length, true);
        break;
      // Misc CHUNKS

      case 'DESC':
        // Description Line
        this.IFF.currentForm.description = this.IFF.reader.getString();
        break;

      case 'TEXT':
      case 'CMNT':
      case 'NCOM':
        this.IFF.currentForm.comment = this.IFF.reader.getString();
        break;
      // Envelope Form

      case 'NAME':
        this.IFF.currentForm.channelName = this.IFF.reader.getString();
        break;
      // Image Map Layer

      case 'WRAP':
        this.IFF.currentForm.wrap = {
          w: this.IFF.reader.getUint16(),
          h: this.IFF.reader.getUint16()
        };
        break;

      case 'IMAG':
        var index = this.IFF.reader.getVariableLengthIndex();
        this.IFF.currentForm.imageIndex = index;
        break;
      // Texture Mapping Form

      case 'OREF':
        this.IFF.currentForm.referenceObject = this.IFF.reader.getString();
        break;

      case 'ROID':
        this.IFF.currentForm.referenceObjectID = this.IFF.reader.getUint32();
        break;
      // Surface Blocks

      case 'SSHN':
        this.IFF.currentSurface.surfaceShaderName = this.IFF.reader.getString();
        break;

      case 'AOVN':
        this.IFF.currentSurface.surfaceCustomAOVName = this.IFF.reader.getString();
        break;
      // Nodal Blocks

      case 'NSTA':
        this.IFF.currentForm.disabled = this.IFF.reader.getUint16();
        break;

      case 'NRNM':
        this.IFF.currentForm.realName = this.IFF.reader.getString();
        break;

      case 'NNME':
        this.IFF.currentForm.refName = this.IFF.reader.getString();
        this.IFF.currentSurface.nodes[this.IFF.currentForm.refName] = this.IFF.currentForm;
        break;
      // Nodal Blocks : connections

      case 'INME':
        if (!this.IFF.currentForm.nodeName) this.IFF.currentForm.nodeName = [];
        this.IFF.currentForm.nodeName.push(this.IFF.reader.getString());
        break;

      case 'IINN':
        if (!this.IFF.currentForm.inputNodeName) this.IFF.currentForm.inputNodeName = [];
        this.IFF.currentForm.inputNodeName.push(this.IFF.reader.getString());
        break;

      case 'IINM':
        if (!this.IFF.currentForm.inputName) this.IFF.currentForm.inputName = [];
        this.IFF.currentForm.inputName.push(this.IFF.reader.getString());
        break;

      case 'IONM':
        if (!this.IFF.currentForm.inputOutputName) this.IFF.currentForm.inputOutputName = [];
        this.IFF.currentForm.inputOutputName.push(this.IFF.reader.getString());
        break;

      case 'FNAM':
        this.IFF.currentForm.fileName = this.IFF.reader.getString();
        break;

      case 'CHAN':
        // NOTE: ENVL Forms may also have CHAN chunk, however ENVL is currently ignored
        if (length === 4) this.IFF.currentForm.textureChannel = this.IFF.reader.getIDTag();else this.IFF.reader.skip(length);
        break;
      // LWO2 Spec chunks: these are needed since the SURF FORMs are often in LWO2 format

      case 'SMAN':
        var maxSmoothingAngle = this.IFF.reader.getFloat32();
        this.IFF.currentSurface.attributes.smooth = maxSmoothingAngle < 0 ? false : true;
        break;
      // LWO2: Basic Surface Parameters

      case 'COLR':
        this.IFF.currentSurface.attributes.Color = {
          value: this.IFF.reader.getFloat32Array(3)
        };
        this.IFF.reader.skip(2); // VX: envelope

        break;

      case 'LUMI':
        this.IFF.currentSurface.attributes.Luminosity = {
          value: this.IFF.reader.getFloat32()
        };
        this.IFF.reader.skip(2);
        break;

      case 'SPEC':
        this.IFF.currentSurface.attributes.Specular = {
          value: this.IFF.reader.getFloat32()
        };
        this.IFF.reader.skip(2);
        break;

      case 'DIFF':
        this.IFF.currentSurface.attributes.Diffuse = {
          value: this.IFF.reader.getFloat32()
        };
        this.IFF.reader.skip(2);
        break;

      case 'REFL':
        this.IFF.currentSurface.attributes.Reflection = {
          value: this.IFF.reader.getFloat32()
        };
        this.IFF.reader.skip(2);
        break;

      case 'GLOS':
        this.IFF.currentSurface.attributes.Glossiness = {
          value: this.IFF.reader.getFloat32()
        };
        this.IFF.reader.skip(2);
        break;

      case 'TRAN':
        this.IFF.currentSurface.attributes.opacity = this.IFF.reader.getFloat32();
        this.IFF.reader.skip(2);
        break;

      case 'BUMP':
        this.IFF.currentSurface.attributes.bumpStrength = this.IFF.reader.getFloat32();
        this.IFF.reader.skip(2);
        break;

      case 'SIDE':
        this.IFF.currentSurface.attributes.side = this.IFF.reader.getUint16();
        break;

      case 'RIMG':
        this.IFF.currentSurface.attributes.reflectionMap = this.IFF.reader.getVariableLengthIndex();
        break;

      case 'RIND':
        this.IFF.currentSurface.attributes.refractiveIndex = this.IFF.reader.getFloat32();
        this.IFF.reader.skip(2);
        break;

      case 'TIMG':
        this.IFF.currentSurface.attributes.refractionMap = this.IFF.reader.getVariableLengthIndex();
        break;

      case 'IMAP':
        this.IFF.reader.skip(2);
        break;

      case 'TMAP':
        this.IFF.debugger.skipped = true;
        this.IFF.reader.skip(length); // needs implementing

        break;

      case 'IUVI':
        // uv channel name
        this.IFF.currentNode.UVChannel = this.IFF.reader.getString(length);
        break;

      case 'IUTL':
        // widthWrappingMode: 0 = Reset, 1 = Repeat, 2 = Mirror, 3 = Edge
        this.IFF.currentNode.widthWrappingMode = this.IFF.reader.getUint32();
        break;

      case 'IVTL':
        // heightWrappingMode
        this.IFF.currentNode.heightWrappingMode = this.IFF.reader.getUint32();
        break;
      // LWO2 USE

      case 'BLOK':
        // skip
        break;

      default:
        this.IFF.parseUnknownCHUNK(blockID, length);
    }

    if (blockID != 'FORM') {
      this.IFF.debugger.node = 1;
      this.IFF.debugger.nodeID = blockID;
      this.IFF.debugger.log();
    }

    if (this.IFF.reader.offset >= this.IFF.currentFormEnd) {
      this.IFF.currentForm = this.IFF.parentForm;
    }
  }
};

THREE.LWO2Parser = LWO2Parser;
} )();
